package demo04

import (
	"context"
	"fmt"
	"math/rand"
	"sync"
	"time"
)

// 真实的贴切的业务场景举例子

// getData模拟数据获取方法，接受一个context参数
func getData(ctx context.Context, id int) (string, error) {
	finishDataChan := make(chan struct{})
	go func(id int) {
		se := rand.Intn(9)
		fmt.Println("模拟耗时操作", se, ",id:", id)
		// 业务场景，请求api拿数据&sql请求等
		//time.Sleep(time.Duration(se))
		time.Sleep(5 * time.Second)
		// 拿到了，发送信号，
		finishDataChan <- struct{}{}
	}(id)

	// 使用select来同时监听context的取消和模拟的耗时操作
	select {
	case <-finishDataChan:
		return fmt.Sprintf("数据 %d 获取成功", id), nil
	case <-ctx.Done():
		return "", fmt.Errorf("获取数据 %d 时被取消: %w", id, ctx.Err())
	}
}

// fetchData在单独的goroutine中调用getData，并处理超时
func fetchData(ctx context.Context, wg *sync.WaitGroup, id int) {
	defer wg.Done()

	// 调用getData并处理结果
	data, err := getData(ctx, id)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(data)
}

func ContextDemo() {
	var wg sync.WaitGroup

	// 创建一个带有3秒超时的context
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel() // 在main函数结束时调用cancel，但超时会自动触发它

	// 启动多个goroutine来模拟并发数据获取
	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go fetchData(ctx, &wg, i)
	}

	// 等待所有goroutine完成（或被取消）
	wg.Wait()
	fmt.Println("所有操作完成或已超时取消")
}
